The symmetry-exceptional slopes of K=t09847 are (-3,1), (-2,1), and (2,1). All three have symmetry group Z/2+Z/2 and thus they can be written in three different ways as DBC. Here we determine all these three ways. Since K is strongly invertible all slopes can be written in one way as DBC branched along a link L in S3. That means for the three symmetry-exceptional fillings we need to find another 2 ways to write them as DBCs.

In [1]:

    import time
    import snappy
    import pandas
    DBChomologies = pandas.read_csv("DBChomologies_non_alternating.csv") # We load a list of all non alternating links in
    #the HTLinkExteriors together with their homologies (if the homology is cyclic) and their crossing numbers.
    ### REMARK: To build the list we need to run the code in the file Build_DBChomologies which takes around 60 minutes.
    #Here we just load it.

    def double_branched_cover(link):
        """
        Returns the double branched cover of the link.
        """
        L=link.copy()
        for i in range(L.num_cusps()):
            L.dehn_fill((2,0),i)
        for cov in L.covers(2):
            if (2.0, 0.0) not in cov.cusp_info('filling'):
                return cov

    def better_is_isometric_to(X,Y,index):
        """
        Returns True if X and Y are isometric.
        Returns False if X and Y have different homologies. TO DO: Use volume to rigorously distinguish X and Y.
        Returns 'unclear' if SnapPy cannot verify it.
        The higher the index the harder SnapPy tries.
        """
        w='unclear'
        if X.homology()!=Y.homology():
            w=False
        if w=='unclear':
            for i in (0,index):
                try:
                    w=X.is_isometric_to(Y)
                except RuntimeError:
                    pass
                except snappy.SnapPeaFatalError:
                    pass
                if w==True:
                    break
                if w==False:
                    w='unclear'
                X.randomize()
                Y.randomize()
                i=i+1
        return w

    def possible_DBC(homologies,max_crossings=15):
        """
        Takes a list of orders of homologies and returns a list consisting of all DBC of alternating links in the HT link table with that homologies together with the link names.
        """
        DBCList=[]
        LINKS=[]
        for order in homologies:
            LINKS=LINKS+DBChomologies.loc[(DBChomologies['homology']==order) & (DBChomologies['crossings']<=max_crossings)]['knot'].tolist()
        for link in LINKS:
            L=snappy.Manifold(link)
            D=double_branched_cover(L)
            DBCList.append([D,link])
        return DBCList

    ### The following two functions are written by Dunfield and search for positive triangulations.

    def all_positive(manifold):
        return manifold.solution_type() == 'all tetrahedra positively oriented'

    def find_positive_triangulation(manifold, tries=100):
        M = manifold.copy()
        for i in range(tries):
            if all_positive(M):
                return M
            M.randomize()
        for d in M.dual_curves():
            X = M.drill(d)
            X = X.filled_triangulation()
            X.dehn_fill((1,0))
            for i in range(tries):
                if all_positive(X):
                    return X
                X.randomize()

        # In the closed case, here is another trick.
        if all(not c for c in M.cusp_info('is_complete')):
            for i in range(tries):
                # Drills out a random edge
                X = M.__class__(M.filled_triangulation())
                if all_positive(X):
                    return X
                M.randomize()

    def better_find_positive_triangulation(M,tries=1):
        '''
        Search for a positive triangulation, but ignores errors.
        '''
        RandomizeCount=0
        while RandomizeCount<tries:
            try:
                X=find_positive_triangulation(M)
                return X
            except snappy.SnapPeaFatalError:
                M.randomize()
                RandomizeCount=RandomizeCount+1
        return None

    def is_alternating(knot,slope,try_hard=False,index=10,tries=1,max_cro=15):
        '''
        Checks if the slope is alternating.
        '''
        K=snappy.Manifold(knot)
        K.dehn_fill(slope)
        DBC=possible_DBC([K.homology().order()],max_crossings=max_cro)
        for D in DBC:
            w=better_is_isometric_to(D[0],K,index)
            if w==True:
                return [slope,D[1]]
        if try_hard:
            X=better_find_positive_triangulation(K,tries)
            if X is not None:
                for D in DBC:
                    Y=better_find_positive_triangulation(D[0],tries)
                    if Y is not None:
                        w=better_is_isometric_to(X,Y,index)
                        if w==True:
                            return [slope,D[1]]
        return False

    ---------------------------------------------------------------------------
    ModuleNotFoundError                       Traceback (most recent call last)
    /var/folders/x0/69y_4spx515cn735q1qy2ylm0000gn/T/ipykernel_60620/2895650112.py in <module>
          1 import time
          2 import snappy
    ----> 3 import pandas
          4 DBChomologies = pandas.read_csv("DBChomologies_non_alternating.csv") # We load a list of all non alternating links in
          5 #the HTLinkExteriors together with their homologies (if the homology is cyclic) and their crossing numbers.

    ModuleNotFoundError: No module named 'pandas'

In [2]:

    K=snappy.Manifold('t09847')
    exc_sym_slopes=[(-3,1),(-2,1),(2,1)]
    for s in exc_sym_slopes:
        K.dehn_fill(s)
        print(s,K.symmetry_group())

    (-3, 1) Z/2 + Z/2
    (-2, 1) Z/2 + Z/2
    (2, 1) Z/2 + Z/2

In [3]:

    start_time = time.time()
    branching_sets=[]
    knot='t09847'
    for slope in exc_sym_slopes:
        w=is_alternating(knot,slope,index=25,max_cro=15)
        if w!=False:
            branching_sets.append([knot,w[0],w[1]])
            print('We found a branching set:',knot,slope,w[1])

    print('Total number of branching sets we have found:', len(branching_sets))
    print('Total time taken: %s minutes ---' % ((time.time() - start_time)/60))

    We found a branching set: t09847 (-3, 1) K15n88871
    We found a branching set: t09847 (-2, 1) L14n24290
    We found a branching set: t09847 (2, 1) L14n31171
    Total number of branching sets we have found: 3
    Total time taken: 0.5895553231239319 minutes ---

Next we will search for branching sets in more general manifolds. For that we will take links in the HT link tables fill some one component of it to get a surgery diagram of a link in a manifold. Then we take the double branched cover of that link and search for a match with a symmetric filling on A.

In [4]:

    DBChomologies_branching = pandas.read_csv("DBChomologies_one_filling.csv")
    ### REMARK: To build the list we need to run the code in the file Build_DBChomologies which takes around 3 days. Here we just load it.

    def double_branched_cover(link):
        """
        Returns the double branched covers of the link. This works also for links in a more general manifold.
        Note that a knot in a more general manifold may have more than one double branched cover
        (or no double branched cover at all if the knot represents a primitive element in homology).
        This function will return the complete list of all double branched covers of the link.
        """
        L=link.copy()
        for i in range(L.num_cusps()):
            if L.cusp_info(i).filling==(0.0, 0.0):
                L.dehn_fill((2,0),i)
        return [cov for cov in L.covers(2) if (2.0, 0.0) not in cov.cusp_info('filling')]

    def possible_DBC_surgery_diagrams(homologies,max_crossings=15):
        """
        Reads off the possible surgery diagrams.
        """
        DBCList=[]
        LINKS=[]
        CUSPS=[]
        SLOPES_STRINGS=[]
        for order in homologies:
            LINKS=LINKS+DBChomologies_branching.loc[(DBChomologies_branching['homology']==order) & (DBChomologies_branching['crossings']<=max_crossings)]['knot'].tolist()
            CUSPS=CUSPS+DBChomologies_branching.loc[(DBChomologies_branching['homology']==order) & (DBChomologies_branching['crossings']<=max_crossings)]['cusp'].tolist()
            SLOPES_STRINGS=SLOPES_STRINGS+DBChomologies_branching.loc[(DBChomologies_branching['homology']==order) & (DBChomologies_branching['crossings']<=max_crossings)]['filling'].tolist()
        SLOPES=[]
        for string in SLOPES_STRINGS:
            string_without_brackets=string[1:-1]
            SLOPES.append(tuple(map(int, string_without_brackets.split(', '))))
        for i in range(0,len(LINKS)):
            DBCList.append([LINKS[i],SLOPES[i],CUSPS[i]])
        return DBCList

    def search_for_branching_set(knot,slope,try_hard=False,index=10,tries=1,max_crossings=15):
        '''
        Searchs for a surgery diagram of the rbanching set
        '''
        K=snappy.Manifold(knot)
        K.dehn_fill(slope)
        DBC=possible_DBC_surgery_diagrams([K.homology().order()],max_crossings)
        for DIAGRAM in DBC:
            L=snappy.Manifold(DIAGRAM[0])
            L.dehn_fill(DIAGRAM[1],DIAGRAM[2])
            for D in double_branched_cover(L):
                w=better_is_isometric_to(D,K,index)
                if w==True:
                    return [[knot,slope,DIAGRAM[0],DIAGRAM[1],DIAGRAM[2]]]
        if try_hard:
            X=better_find_positive_triangulation(K,tries)
            if X is not None:
                for DIAGRAM in DBC:
                    L=snappy.Manifold(DIAGRAM[0])
                    L.dehn_fill(DIAGRAM[1],DIAGRAM[2])
                    for D in double_branched_cover(L):
                        Y=better_find_positive_triangulation(D,tries)
                        if Y is not None:
                            w=better_is_isometric_to(X,Y,index)
                            if w==True:
                                return [[knot,slope,DIAGRAM[0],DIAGRAM[1],DIAGRAM[2]]]
        return False

    def search_for_three_branching_sets(knot,slope,try_hard=False,index=10,tries=1,max_crossings=15):
        '''
        Searchs for a surgery diagram of the branching set
        '''
        BRANCHING_SETS=[]
        K=snappy.Manifold(knot)
        K.dehn_fill(slope)
        homologies=[]
        DBC=possible_DBC_surgery_diagrams([K.homology().order()],max_crossings)
        for DIAGRAM in DBC:
            L=snappy.Manifold(DIAGRAM[0])
            L.dehn_fill(DIAGRAM[1],DIAGRAM[2])
            for D in double_branched_cover(L):
                w=better_is_isometric_to(D,K,index)
                if w==True:
                    BRANCHING_SETS=BRANCHING_SETS+[[knot,slope,DIAGRAM[0],DIAGRAM[1],DIAGRAM[2]]]
                    for i in range(L.num_cusps()):
                        if L.cusp_info(i).filling==(0.0, 0.0):
                            L.dehn_fill((1,0),i)
                    if L.homology().order() not in homologies:
                        homologies.append(L.homology().order())
                    if len(homologies)==3:
                        return BRANCHING_SETS
        if try_hard:
            X=better_find_positive_triangulation(K,tries)
            if X is not None:
                for DIAGRAM in DBC:
                    L=snappy.Manifold(DIAGRAM[0])
                    L.dehn_fill(DIAGRAM[1],DIAGRAM[2])
                    for D in double_branched_cover(L):
                        Y=better_find_positive_triangulation(D,tries)
                        if Y is not None:
                            w=better_is_isometric_to(X,Y,index)
                            if w==True:
                                BRANCHING_SETS=BRANCHING_SETS+[[knot,slope,DIAGRAM[0],DIAGRAM[1],DIAGRAM[2]]]
                                for i in range(L.num_cusps()):
                                    if L.cusp_info(i).filling==(0.0, 0.0):
                                        L.dehn_fill((1,0),i)
                                if L.homology().order() not in homologies:
                                    homologies.append(L.homology().order())
                                if len(homologies)==3:
                                    return BRANCHING_SETS
        return BRANCHING_SETS

In [5]:

    start_time = time.time()
    knot='t09847'
    for slope in exc_sym_slopes:
        w=search_for_three_branching_sets(knot,slope,index=3,max_crossings=15)
        if w==[]:
            w=False
        if w!=False:
            branching_sets=branching_sets+w
            print('We found a branching set:',w)

    print('Total number of branching sets we have found:', len(branching_sets))
    print('Total time taken: %s minutes ---' % ((time.time() - start_time)/60))

    We found a branching set: [['t09847', (-3, 1), 'L13n5917', (-3, 1), 0], ['t09847', (-3, 1), 'L14n24778', (-3, 1), 0]]
    We found a branching set: [['t09847', (-2, 1), 'L11n420', (5, 3), 0], ['t09847', (-2, 1), 'L13n7290', (-2, 1), 1], ['t09847', (-2, 1), 'L13n9355', (-5, 2), 1], ['t09847', (-2, 1), 'L13n9861', (2, 1), 1]]
    We found a branching set: [['t09847', (2, 1), 'L12n1082', (-2, 1), 0]]
    Total number of branching sets we have found: 10
    Total time taken: 112.62691159645716 minutes ---

In [6]:

    branching_sets

Out[6]:

    [['t09847', (-3, 1), 'K15n88871'],
     ['t09847', (-2, 1), 'L14n24290'],
     ['t09847', (2, 1), 'L14n31171'],
     ['t09847', (-3, 1), 'L13n5917', (-3, 1), 0],
     ['t09847', (-3, 1), 'L14n24778', (-3, 1), 0],
     ['t09847', (-2, 1), 'L11n420', (5, 3), 0],
     ['t09847', (-2, 1), 'L13n7290', (-2, 1), 1],
     ['t09847', (-2, 1), 'L13n9355', (-5, 2), 1],
     ['t09847', (-2, 1), 'L13n9861', (2, 1), 1],
     ['t09847', (2, 1), 'L12n1082', (-2, 1), 0]]

Since the knot K is strongly invertible we have always at least on way to write the knot as a DBC over a link in S3. The first 3 branching sets in S3 are these generic branching sets. The other surgery descriptions yield surgery descriptions in lens spaces. We still need to find one more branching set for (-3,1) and one more for (2,1). For that we search for surgery descriptions where we fill two surgery curves.

In [3]:

    K16=snappy.Manifold('t09847(2,1)')
    K16

Out[3]:

    t09847(2,1)

In [4]:

    K16.homology()

Out[4]:

    Z/16

In [5]:

    K21=snappy.Manifold('t09847(-3,1)')
    K21

Out[5]:

    t09847(-3,1)

In [6]:

    K21.homology()

Out[6]:

    Z/21

In [7]:

    def better_is_isometric_to(X,Y,index=10,return_isometries=False):
        """
        Returns True if X and Y are isometric.
        Returns False if it is unclear.
        """
        w=False
        for i in (0,index):
            try:
                w=X.is_isometric_to(Y,return_isometries)
                return w
            except (RuntimeError, snappy.SnapPeaFatalError):
                X.randomize()
                Y.randomize()
                i=i+1
        return w

    def gcd(a, b):
        while b != 0:
            a, b = b, a % b
        return a

    def coprime(a, b):
        return gcd(a, b) == 1

    slope_set=[]
    for q in range (1,5):
        for p in range (2,11):
            if coprime(p,q)==True:
                slope_set.append((p,q))
                slope_set.append((-p,q))

    def double_branched_cover(link):
        """
        Returns the double branched covers of the link. This works also for links in a more general manifold.
        Note that a knot in a more general manifold may have more than one double branched cover
        (or no double branched cover at all if the knot represents a primitive element in homology).
        This function will return the complete list of all double branched covers of the link.
        """
        L=link.copy()
        for i in range(L.num_cusps()):
            if L.cusp_info(i).filling==(0.0, 0.0):
                L.dehn_fill((2,0),i)
        return [cov for cov in L.covers(2) if (2.0, 0.0) not in cov.cusp_info('filling')]

In [18]:

    start_time = time.time()

    for L in snappy.HTLinkExteriors(knots_vs_links='links'):
        if L.num_cusps()==3:
            for r in slope_set:
                for s in slope_set:
                    L.dehn_fill([r,s,(0,0)])
                    DBC=double_branched_cover(L)
                    for D in DBC:
                        hom=D.homology()
                        if len(hom)==1:
                            if D.homology().coefficients==[16]:
                                if better_is_isometric_to(D,K16):
                                    print(L)
                            if D.homology().coefficients==[21]:
                                if better_is_isometric_to(D,K21):
                                    print(L)
                    L.dehn_fill([r,(0,0),s])
                    DBC=double_branched_cover(L)
                    for D in DBC:
                        hom=D.homology()
                        if len(hom)==1:
                            if D.homology().coefficients==[16]:
                                if better_is_isometric_to(D,K16):
                                    print(L)
                            if D.homology().coefficients==[21]:
                                if better_is_isometric_to(D,K21):
                                    print(L)
                    L.dehn_fill([(0,0),r,s])
                    DBC=double_branched_cover(L)
                    for D in DBC:
                        hom=D.homology()
                        if len(hom)==1:
                            if D.homology().coefficients==[16]:
                                if better_is_isometric_to(D,K16):
                                    print(L)
                            if D.homology().coefficients==[21]:
                                if better_is_isometric_to(D,K21):
                                    print(L)

    print("--- Time taken: %s hours ---" % (time.time() - start_time)/3600)

    L10n92(0,0)(-7,4)(-8,3)
    L11n294(-2,3)(-5,1)(0,0)
    L11n420(0,0)(7,3)(4,3)

    ---------------------------------------------------------------------------
    NameError                                 Traceback (most recent call last)
    /var/folders/x0/69y_4spx515cn735q1qy2ylm0000gn/T/ipykernel_58315/2258601642.py in <module>
         16                         if D.homology().coefficients==[Integer(21)]:
         17                             if better_is_isometric_to(D,K21):
    ---> 18                                 print(L)
         19                 L.dehn_fill([r,(Integer(0),Integer(0)),s])
         20                 DBC=double_branched_cover(L)

    cython/core/triangulation.pyx in SnapPy.Triangulation.__repr__()

    cython/core/manifold.pyx in SnapPy.Manifold.cusp_info()

    ~/.sage/local/lib/python3.10/site-packages/snappy/__init__.py in <lambda>(n)
        121 from .sage_helper import _within_sage
        122 if _within_sage:
    --> 123     to_sage = lambda n : n.sage()
        124     Manifold.use_field_conversion(to_sage)
        125     ManifoldHP.use_field_conversion(to_sage)

    ~/.sage/local/lib/python3.10/site-packages/snappy/number.py in sage(self)
        534             return RealField(self._precision)(self.gen)
        535         elif self.gen.type() == 't_COMPLEX':
    --> 536             return ComplexField(self._precision)(self.gen)
        537         else:
        538             return self.gen.sage()

    /private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/rings/complex_mpfr.pyx in sage.rings.complex_mpfr.ComplexField_class.__call__ (build/cythonized/sage/rings/complex_mpfr.c:7336)()
        482         if im is not None:
        483             x = x, im
    --> 484         return Parent.__call__(self, x)
        485
        486     def _element_constructor_(self, x):

    /private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/structure/parent.pyx in sage.structure.parent.Parent.__call__ (build/cythonized/sage/structure/parent.c:9450)()
        895         if mor is not None:
        896             if no_extra_args:
    --> 897                 return mor._call_(x)
        898             else:
        899                 return mor._call_with_args(x, args, kwds)

    /private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/structure/coerce_maps.pyx in sage.structure.coerce_maps.DefaultConvertMap_unique._call_ (build/cythonized/sage/structure/coerce_maps.c:4734)()
        159                 print(type(C), C)
        160                 print(type(C._element_constructor), C._element_constructor)
    --> 161             raise
        162
        163     cpdef Element _call_with_args(self, x, args=(), kwds={}):

    /private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/structure/coerce_maps.pyx in sage.structure.coerce_maps.DefaultConvertMap_unique._call_ (build/cythonized/sage/structure/coerce_maps.c:4626)()
        154         cdef Parent C = self._codomain
        155         try:
    --> 156             return C._element_constructor(x)
        157         except Exception:
        158             if print_warnings:

    /private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/rings/complex_mpfr.pyx in sage.rings.complex_mpfr.ComplexField_class._element_constructor_ (build/cythonized/sage/rings/complex_mpfr.c:8251)()
        533
        534             try:
    --> 535                 return self(x.sage())
        536             except (AttributeError, TypeError):
        537                 pass

    cypari2/gen.pyx in cypari2.gen.Gen.sage()

    /private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/libs/pari/convert_sage.pyx in sage.libs.pari.convert_sage.gen_to_sage (build/cythonized/sage/libs/pari/convert_sage.c:4800)()
         39
         40
    ---> 41 cpdef gen_to_sage(Gen z, locals=None):
         42     """
         43     Convert a PARI gen to a Sage/Python object.

    /private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/libs/pari/convert_sage.pyx in sage.libs.pari.convert_sage.gen_to_sage (build/cythonized/sage/libs/pari/convert_sage.c:4034)()
        296         else:
        297             K = QuadraticField(-1, 'i')
    --> 298             return K([gen_to_sage(real), gen_to_sage(imag)])
        299     elif t == t_VEC or t == t_COL:
        300         return [gen_to_sage(x, locals) for x in z.python_list()]

    /private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/libs/pari/convert_sage.pyx in sage.libs.pari.convert_sage.gen_to_sage (build/cythonized/sage/libs/pari/convert_sage.c:4666)()
        324     from sage.misc.sage_eval import sage_eval
        325     locals = {} if locals is None else locals
    --> 326     return sage_eval(str(z), locals=locals)
        327
        328

    /private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/misc/sage_eval.py in sage_eval(source, locals, cmds, preparse)
        196         return locals['_sage_eval_returnval_']
        197     else:
    --> 198         return eval(source, sage.all.__dict__, locals)
        199
        200

    /private/var/tmp/sage-9.6-current/local/var/lib/sage/venv-python3.10.3/lib/python3.10/site-packages/sage/all.py in <module>

    NameError: name 'nan' is not defined

The first two surgery descriptions yield the two different manifold. And we can check via KLO that the yield links in different lens spaces.

In [9]:

    B21=snappy.Manifold('L11n294(-2,3)(-5,1)(0,0)')
    D=double_branched_cover(B21)[0]
    D.homology()

Out[9]:

    Z/21

In [11]:

    D.is_isometric_to(K21)

Out[11]:

    True

In [12]:

    B16=snappy.Manifold('L10n92(0,0)(-7,4)(-8,3)')
    D=double_branched_cover(B16)[0]
    D.homology()

Out[12]:

    Z/16

In [13]:

    D.is_isometric_to(K16)

Out[13]:

    True